#pragma once

#include "my_conn.h"
#include "comm/my_hrtime.h"
#include "comm/my_cleanup.h"
#include "comm/my_thread_base.h"

typedef struct my_event_loop_data_s my_event_loop_data_t;

typedef void (*my_event_loop_init_handle_pt)(my_event_loop_data_t *loop_data);
typedef void (*my_event_loop_quit_handle_pt)(my_event_loop_data_t *loop_data);

struct my_event_loop_data_s {
    my_event_loop_init_handle_pt    init_handle;
    my_event_loop_quit_handle_pt    quit_handle;

    ////////// External queue /////////////
    my_spinlock_t                   lock;
    my_queue_t                      external_queue;

    //////////// Epoll attribute //////////
    int                             epfd;
    my_conn_t                       notify_conn;    // for wakeup epoll wait
    int                             nevents;
    my_epoll_event_t                *event_list;

    ///////////// Local queue /////////////
    // Local queue is a lock-free queue.
    // Move tasks in external queue to this queue at one time
    // This can reduce the contention of external queue.
    my_queue_t                      local_queue;

    // It's a rbtree, for timer task.
    my_rbtree_t                     timer_rbtree;
    my_rbtree_node_t                timer_sentinel;

    // It's a ready queue.
    my_queue_t                      ready_queue;

    // For event loop quit.
    void                            *data;
    int64_t                         conn_nums;
    int64_t                         reserve_conns;
    my_cleanup_list_t               cleanup_list;

    my_atomic_bool_t                quit;
    unsigned                        stop:1;
};

static inline void
my_event_loop_data_init(my_event_loop_data_t *loop_data,
    my_event_loop_init_handle_pt init_handle,
    my_event_loop_quit_handle_pt quit_handle,
    int nevents)
{
    loop_data->init_handle = init_handle;
    loop_data->quit_handle = quit_handle;

    my_spinlock_init(&loop_data->lock);
    my_queue_init(&loop_data->external_queue);

    my_queue_init(&loop_data->local_queue);

    my_rbtree_init(&loop_data->timer_rbtree, &loop_data->timer_sentinel,
        my_event_timer_cmp);

    loop_data->epfd = INVALID_FD;
    my_conn_init(&loop_data->notify_conn, MY_CONN_EPOLL_LT);
    loop_data->nevents = nevents;
    loop_data->event_list = NULL;

    my_queue_init(&loop_data->ready_queue);

    my_cleanup_list_init(&loop_data->cleanup_list);
}

void my_event_loop_runner(my_thread_base_t *thread_base);
void my_event_loop_quit(my_thread_base_t *thread_base);
void my_event_loop_post_event(my_thread_base_t *thread_base,
    my_dispatch_event_t *dispatch_event);

void my_event_loop_add_timer(my_event_t *event, int64_t timeout_ms);
void my_event_loop_del_timer(my_event_t *event);

my_bool_t my_event_loop_start_io(my_conn_t *conn);
my_bool_t my_event_loop_stop_io(my_conn_t *conn);
my_bool_t my_event_loop_enabled_io(my_conn_t *conn, my_bool_t write);
my_bool_t my_event_loop_disabled_io(my_conn_t *conn, my_bool_t write);
void my_event_loop_remove_conn(my_conn_t *conn);